package com.hexinfo.dmpro.component.code.detection.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.hdtp.common.utils.AntlrUtil;
import com.hexinfo.dmpro.common.utils.StringUtils;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveLexer;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveParser;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveParserBaseVisitor;
import com.hexinfo.dmpro.component.code.detection.enums.AntlrEnum;
import com.hexinfo.dmpro.component.code.detection.utils.JsonReadUtil;
import com.hexinfo.dmpro.component.code.detection.utils.SqlFormatterUtil;
import lombok.extern.slf4j.Slf4j;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class HiveTableParamSqlVisitor extends HiveParserBaseVisitor<String> {

    public static final String MARK_BEGIN = "/*";
    public static final String MARK_END = "*/";
    public static final String MARK_CODE = "[CODE]";
    public static final String ERROR_CODE = "[ERROR]";
    public static final String WARN_CODE = "[WARN]";
    public static final String ERROR_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, ERROR_CODE);
    public static final String WARN_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, WARN_CODE);

    private String fileName = "";
    // 动态配置文件信息
    Map<String,String> config = new HashMap<>();
    private Boolean result = true;
    public Boolean getResult() { return result; }

    public String visitRoot(String fileName, final String sourceSql, Map<String,String> config) {
        final CodePointCharStream input = CharStreams.fromString(sourceSql);
        this.fileName = fileName;
        this.config = config;
        HiveLexer lexer = new HiveLexer(input);
        CommonTokenStream token = new CommonTokenStream(lexer);
        HiveParser parser = new HiveParser(token);
        String resultStr = this.visit(parser.statements());
        return SqlFormatterUtil.formatterSqlWithSpace(resultStr);
    }

    @Override
    public String visitAtomSelectStatement(HiveParser.AtomSelectStatementContext ctx) {
        try {
            //        List<String> ignoreSql = JsonReadUtil.castList(objectConfig.get("ignoreSql"), String.class);
            List<String> ignoreSql = getParamListUtil(config,"ignoreSql");
            List<String> compactSql = CollectionUtil.isEmpty(ignoreSql) ? null : ignoreSql.stream().map(s -> StringUtils.replaceChars(s, " \n\r\t", "")).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(compactSql) && CollectionUtil.contains(compactSql, ctx.getText())) {
                return "";
            } else {
                return super.visitAtomSelectStatement(ctx);
            }
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at visitAtomSelectStatement=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at visitAtomSelectStatement=============================================");
        }
    }

    @Override
    public String visitTableSource(HiveParser.TableSourceContext ctx) {
        try {
            String resultStr = super.visitTableSource(ctx);
            // 校验库名是否参数化
            resultStr = checkDbNameParametric(resultStr, ctx);
            return resultStr;
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at visitTableSource=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at visitTableSource=============================================");
        }

    }

    @Override
    public String visitTerminal(TerminalNode node) {
        try {
            if (!node.getText().equals("<EOF>")) {
                return node.getText();
            }
            return super.visitTerminal(node);
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at visitTableSource=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at visitTableSource=============================================");
        }

    }

    @Override
    public String visitErrorNode(ErrorNode node) {
        log.info("推送下载稽核Value中语句不是Impala的，转为Oracle校验");
        throw new RuntimeException("推送下载稽核Value中语句不是Impala的，转为Oracle校验");
    }

    private String checkDbNameParametric(String resultStr, HiveParser.TableSourceContext ctx) {
        try {
            // 判断其是否存在tableName子节点  且  tableName.identifier存在，且存在库名.表名这种格式
            if (AntlrUtil.queryDesignateChildren(ctx, HiveParser.TableNameContext.class)
                    && ctx.tableName().identifier(0) != null && ctx.tableName().DOT() != null) {
                List<String> paramsList = getServiceParam(fileName);
                if (CollectionUtil.isNotEmpty(paramsList) && !paramsList.contains(ctx.tableName().identifier(0).getText())) {
                    this.result = false;
                    resultStr = resultStr + getMarkResult(AntlrEnum.VALUE_DB_NAME.getDesc(), "necessary");
                }
            }
            return resultStr;
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at checkDbNameParametric=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at checkDbNameParametric=============================================");
        }
    }

    private List<String> getServiceParam(String tableName) {
        try {
            switch (tableName.toUpperCase()) {
                case "CTL_LOAD_CONFIG":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_ctlLoadConfig"), String.class);
                    return getParamListUtil(config,"serviceParam_ctlLoadConfig");
                case "CTL_AUDIT_PARAM":
                case "CTL_AUDIT_PARAM_NEW":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_ctlAuditParam"), String.class);
                    return getParamListUtil(config,"serviceParam_ctlAuditParam");
                case "TPUSHLIST":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_tpushlist"), String.class);
                    return getParamListUtil(config,"serviceParam_tpushlist");
                default:
                    return null;
            }
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at getServiceParam=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at getServiceParam=============================================");
        }
    }
    /**
     * @Package com.hexinfo.dmpro.component.code.detection.service
     * @ClassName OracleTableParamSqlVisitor
     * @Description 根据beforeParam对多参数进行切割返回List集合
     * @Author le.zhang
     * @Date 2024/3/4 10:02
     * @Version v1.0
     **/
    public List<String> getParamListUtil(Map<String,String> paramsMap,String beforeParam){
        try {
            String s = paramsMap.get(beforeParam);
            if (null == s) {
                return new ArrayList<>();
            }
            String[] split = s.split(",");
            List<String> resultList = Arrays.asList(split);
            return resultList;
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at getParamListUtil=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at getParamListUtil=============================================");
        }

    }

    /**
     * @Method getMarkResult
     * @Param
     * @param scanName
     * @param scanLevel
     * @Return java.lang.String
     * @Description 获取SQL中的不同等级异常标记信息
     * @Author Wang zhihao
     * @Date 2024/2/6 13:33
     * @Version V1.0
     */
    private String getMarkResult(String scanName, String scanLevel) {
        try {
            switch (scanLevel.toUpperCase(Locale.ROOT)) {
                case "WARNING":
                    return StrUtil.format("{} {} {}", WARN_MARK_BEGIN, scanName, MARK_END);
                case "NECESSARY":
                    return StrUtil.format("{} {} {}", ERROR_MARK_BEGIN, scanName, MARK_END);
                default:
                    return "";
            }
        } catch (Exception e) {
            log.error("【ANTLR-CATCH】catch at getMarkResult=============================================");
            throw new RuntimeException("【ANTLR-CATCH】catch at getMarkResult=============================================");
        }
    }
}
